package ar.com.spsolutions.splibrary.repositories;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import ar.com.spsolutions.splibrary.entity.IEntity;

/**
 * Generic hibernate DAO
 * 
 * @param <T>
 */
public abstract class HibernateGenericDAO<T extends IEntity> extends
		HibernateDaoSupport implements GenericRepository<T> {

	private static final long serialVersionUID = -3221496197751806882L;
	private static final String UNCHECKED = "unchecked";
	protected transient Class<T> persistentClass;

	public HibernateGenericDAO(final Class<T> domainClass) {
		super();
		this.persistentClass = domainClass;
	}

	@Override
	public void save(final T entity) {
		this.getHibernateTemplate().save(entity);
		this.getHibernateTemplate().flush();
	}

	@Override
	public void delete(final T entity) {
		this.getHibernateTemplate().delete(entity);
	}

	@Override
	public T update(final T entity) {
		this.getHibernateTemplate().update(entity);
		return entity;
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findById(final Serializable id) {
		return (T) this.getHibernateTemplate().get(this.persistentClass, id);
	}

	@Override
	@SuppressWarnings(HibernateGenericDAO.UNCHECKED)
	public List<T> findAll() {
		return this.getHibernateTemplate().find(
				"from " + this.persistentClass.getName() + " o");

	}

	@Override
	public void deleteById(final Serializable id) {
		final T obj = this.findById(id);
		this.getHibernateTemplate().delete(obj);
	}

	@Override
	@SuppressWarnings(HibernateGenericDAO.UNCHECKED)
	public int count() {
		final List<Long> list = this.getHibernateTemplate()
				.find("select count(*) from " + this.persistentClass.getName()
						+ " o");
		final Long count = list.get(0);
		return count.intValue();

	}

	@Override
	@SuppressWarnings(HibernateGenericDAO.UNCHECKED)
	public List<T> findByExample(final T exampleObject) {
		return this.getHibernateTemplate().findByExample(exampleObject);

	}

	@SuppressWarnings({ HibernateGenericDAO.UNCHECKED })
	protected T findByEq(final String property, final Object value) {
		final DetachedCriteria criteria = this.createDetachableCriteria();
		criteria.add(Restrictions.eq(property, value));
		final List<T> result = this.getHibernateTemplate().findByCriteria(
				criteria);
		return result.isEmpty() ? null : result.get(0); // NOPMD
	}

	@SuppressWarnings({ HibernateGenericDAO.UNCHECKED })
	protected List<T> findAllByEq(final String property, final Object value) {
		final DetachedCriteria criteria = this.createDetachableCriteria();
		criteria.add(Restrictions.eq(property, value));
		return this.getHibernateTemplate().findByCriteria(criteria);
	}

	protected DetachedCriteria createDetachableCriteria() {
		final DetachedCriteria criteria = DetachedCriteria
				.forClass(this.persistentClass);
		return criteria;
	}

	@SuppressWarnings(HibernateGenericDAO.UNCHECKED)
	protected List<T> findAllByEq(final String property, final String value) {
		final DetachedCriteria criteria = this.createDetachableCriteria();
		criteria.add(Restrictions.eq(property, value));
		return this.getHibernateTemplate().findByCriteria(criteria);
	}

	protected Criteria createCriteria() {
		final SessionFactory sessionFactory = this.getHibernateTemplate()
				.getSessionFactory();
		final Session currentSession = sessionFactory.getCurrentSession();
		final Criteria criteria = currentSession
				.createCriteria(this.persistentClass);
		return criteria;
	}

	@Override
	public T attachSession(final T entity) {
		if ((entity != null) && (entity.getId() != null)) {
			this.update(entity);
		}
		return entity;
	}

	@Override
	@SuppressWarnings(HibernateGenericDAO.UNCHECKED)
	public List<T> findByLike(final String property, final String value) {
		final DetachedCriteria criteria = this.createDetachableCriteria();
		criteria.add(Restrictions.ilike(property, value, MatchMode.ANYWHERE));
		return this.getHibernateTemplate().findByCriteria(criteria);
	}

	@Override
	public T get(final T entity) {
		final Integer id = entity.getId();
		if (id == null) {
			return entity;
		}
		return this.findById(id);
	}

}